package com.apes.scm.masterdata.product.es;

import com.apes.framework.api.DomainService;
import com.apes.framework.es.EsDsl;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.jpa.spec.model.Group;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.outbox.annotation.OutboxSubscriber;
import com.apes.framework.util.DateUtil;
import com.apes.framework.util.PinyinUtil;
import com.apes.framework.util.ReflectUtil;
import com.apes.scm.masterdata.product.model.Category;
import com.apes.scm.masterdata.product.repository.CategoryRepository;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryAction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

@Service("esCategoryService")
public class EsCategoryService extends DomainService {

    @Autowired
    private ElasticsearchClient client;

    @Autowired
    private EsCategoryRepository repository;

    @Autowired
    private CategoryRepository categoryRepository;

    @Value("${apes.es.tags:}")
    private String tags;

    public void init() {
        AtomicInteger page = new AtomicInteger();
        Sort sort = new Sort(Sort.Direction.ASC, "id");
        while (true) {
            List<Category> categories = categoryRepository.findAll(PageRequest.of(page.get(), 1000, sort)).getContent();
            categories.stream().forEach(category -> {
                save(category);
            });
            if (categories.size() < 1000) {
                break;
            }
            page.getAndIncrement();
        }
    }

    public void increment(SimpleRequest request) {
        int size = 100;
        Map row = request.getMap();
        int expire = row.containsKey("expire") ? Integer.valueOf(String.valueOf(row.get("expire"))) : 10; /**单位：分钟**/
        Date updateDate = DateUtil.add(new Date(), Calendar.MINUTE, -1 * expire);
        while (true) {
            List<Category> categories = categoryRepository.findAllByUpdateDateGreaterThanOrderByIdAsc(updateDate, PageRequest.of(0, size));
            categories.stream().forEach(category -> save(category));
            if (categories.size() < size) {
                break;
            }
            updateDate = categories.get(categories.size() - 1).getUpdateDate();
        }
    }

    public void save(Category category) {
        delete(category);
        List esCategories = new ArrayList();
        EsCategory esCategory = new EsCategory();
        ReflectUtil.copyAll(category, esCategory, "id");
        esCategory.setCategoryId(category.getId());
        esCategory.setInitials(getShortPinyin(esCategory.getName()));
        if (StringUtils.hasText(tags)) {
            esCategory.setTags(tags);
        }
        esCategories.add(esCategory);
        category.getCategoryTags().stream().forEach(tag -> {
            EsCategory esc = new EsCategory();
            ReflectUtil.copyAll(category, esc, "id", "name");
            esc.setCategoryId(category.getId());
            esc.setName(tag.getName());
            esc.setInitials(getShortPinyin(tag.getName()));
            if (StringUtils.hasText(tags)) {
                esc.setTags(tags);
            }
            esCategories.add(esc);
        });
        repository.saveAll(esCategories);
    }

    private String getShortPinyin(String name) {
        return PinyinUtil.getShortPinyin(name).replaceAll(" ", "");
    }

    @OutboxSubscriber(name = "配件名称导入搜索引擎：删除", eventType = "D")
    public void delete(Category category) {
        try {
            if (StringUtils.hasText(tags)) {
                repository.deleteAllByTagsAndCategoryId(tags, category.getId());
            } else {
                repository.deleteAllByCategoryId(category.getId());
            }
        } catch (IndexNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void deleteAllByCategoryId(String categoryId) {
        try {
            String index = ReflectUtil.getAnnotation(EsCategory.class, Document.class).indexName();
            BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
            queryBuilder.must(QueryBuilders.termQuery("categoryId", categoryId));
            if (StringUtils.hasText(tags)) {
                queryBuilder.must(QueryBuilders.termQuery("tags", tags));
            }
            DeleteByQueryAction.INSTANCE.newRequestBuilder(client).filter(queryBuilder).source(index).get().getDeleted();
        } catch (IndexNotFoundException e) {
            e.printStackTrace();
        }
    }

    public Page<Category> findByName(SimpleRequest request) {
        List<String> categoryIds = new ArrayList<>();
        String content = request.get("name");
        Group group = createFilters(content);
        QueryBuilder qb = EsDsl.build(group, null);
        List<EsCategory> esCategories = repository.search(qb, PageRequest.of(0, 1000)).getContent();
        esCategories.stream().forEach(esc -> {
            if (!categoryIds.contains(esc.getCategoryId())) {
                categoryIds.add(esc.getCategoryId());
            }
        });
        return (Page<Category>) paged(categoryIds, request.get("page"), request.get("size"), categoryId -> categoryRepository.findOne((String) categoryId));
    }

    private Group createFilters(String content) {
        Group group = new Group();
        if (StringUtils.hasText(tags)) {
            group.setFilters(JpaDsl.toFilter(
                    "matchCProduct", "EQ", true,
                    "valid", "EQ", true,
                    "tags", "LIKE", tags
                    ));
        } else {
            group.setFilters(JpaDsl.toFilter(
                    "matchCProduct", "EQ", true,
                    "valid", "EQ", true));
        }

        Group g = new Group();
        g.setOperator("or");
        g.setFilters(JpaDsl.toFilter(
                "categoryId", "EQ", content,
                "name", "LIKE", content));
        group.addGroup(g);
        return group;
    }
}
